/*
 * Copyright 2025 GuoTingjin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

/**
 * @class TSUtil
 * @author GuoTingjin
 * @tutorial
 * To use restricted Object related functions, call TSUtil.[key]
 */
export class TSUtil {
  /**
   * Prevents the modification of existing property attributes and values, and prevents the addition of new properties.
   * @param o Object on which to lock the attributes.
   */
  public static freeze<T>(o: T): Readonly<T> {
    return Object.freeze(o)
  }

  /**
   * Returns the names of the own properties of an object. The own properties of an object are those that are defined directly
   * on that object, and are not inherited from the object's prototype. The properties of an object include both fields (objects) and functions.
   * @param o Object that contains the own properties.
   */
  public static getOwnPropertyNames<T>(o: T): Array<string> {
    return Object.getOwnPropertyNames(o)
  }

  /**
   * Returns the prototype of an object.
   * @param o The object that references the prototype.
   */
  public static getPrototypeOf<T>(o: T) {
    return Object.getPrototypeOf(o)
  }

  /**
   * Gets the own property descriptor of the specified object.
   * An own property descriptor is one that is defined directly on the object and is not inherited from the object's prototype.
   * @param o Object that contains the property.
   * @param p Name of the property.
   */
  public static getOwnPropertyDescriptor<T>(o: T, p: PropertyKey): PropertyDescriptor | undefined {
    return Object.getOwnPropertyDescriptor(o, p)
  }

  /**
   * Adds a property to an object, or modifies attributes of an existing property.
   * @param o Object on which to add or modify the property. This can be a native JavaScript object (that is, a user-defined object or a built in object) or a DOM object.
   * @param p The property name.
   * @param attributes Descriptor for the property. It can be for a data property or an accessor property.
   */
  public static defineProperty<T>(o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType<T>): T {
    return Object.defineProperty(o, p, attributes)
  }

  /**
   * Prevents the modification of attributes of existing properties, and prevents the addition of new properties.
   * @param o Object on which to lock the attributes.
   */
  public static seal<T>(o: T): T {
    return Object.seal(o)
  }

  /**
   * Prevents the addition of new properties to an object.
   * @param o Object to make non-extensible.
   */
  public static preventExtensions<T>(o: T): T {
    return Object.preventExtensions(o)
  }

  /**
   * Returns true if existing property attributes cannot be modified in an object and new properties cannot be added to the object.
   * @param o Object to test.
   */
  public static isSealed<T>(o: T): boolean {
    return Object.isSealed(o)
  }

  /**
   * Returns true if existing property attributes and values cannot be modified in an object, and new properties cannot be added to the object.
   * @param o Object to test.
   */
  public static isFrozen<T>(o: T): boolean {
    return Object.isFrozen(o)
  }

  /**
   * Returns a value that indicates whether new properties can be added to an object.
   * @param o Object to test.
   */
  public static isExtensible<T>(o: T): boolean {
    return Object.isExtensible(o)
  }

  /**
   * Copy the values of all of the enumerable own properties from one or more source objects to a
   * target object. Returns the target object.
   * @param target The target object to copy to.
   * @param source The source object from which to copy properties.
   */
  public static assign<T, K>(target: T, source: K): T & K {
    return Object.assign(target, source)
  }

  /**
   * Returns an object created by key-value entries for properties and methods
   * @param entries An iterable object that contains key-value entries for properties and methods.
   */
  public static fromEntries<T>(entries: Iterable<readonly [PropertyKey, T]>): { [k: string]: T }

  /**
   * Returns an object created by key-value entries for properties and methods
   * @param entries An iterable object that contains key-value entries for properties and methods.
   */
  public static fromEntries<T>(entries: Iterable<readonly any[]>): { [k: string]: T }

  public static fromEntries<T>(entries: Iterable<readonly any[]> | Iterable<readonly [PropertyKey, T]>): { [k: string]: T } {
    return Object.fromEntries(entries)
  }
}